package org.jeecg.module.flow.meter.tcpserver.factory;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.jeecg.module.flow.meter.tcpserver.dto.Cat1MessageDto;
import org.jeecg.module.flow.meter.tcpserver.entity.*;
import org.jeecg.module.flow.meter.tcpserver.service.*;
import org.jeecg.module.flow.meter.tcpserver.utils.FlowMeterUtils;
import org.jeecg.module.flow.meter.tcpserver.utils.RedisUtil;
import org.jeecg.module.flow.meter.tcpserver.utils.RestUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ConcurrentLinkedQueue;

import static org.jeecg.module.flow.meter.tcpserver.utils.FlowMeterUtils.*;

/**
 * @Author: 86176
 * @Date: 2024-05-21 14:30
 * @Version: 1.0
 */
@Component
@Slf4j
public class FlowMeterMessageFactory {

    @Autowired
    private IMeterDeviceService meterDeviceService;
    @Autowired
    private IMeterService meterService;
    @Autowired
    private IMeterDeviceDataLogService meterDeviceDataLogService;
    @Autowired
    private IMeterAlarmLogService meterAlarmLogService;

    public void parseMessage(Cat1MessageDto cat1MessageDto, ChannelHandlerContext ctx) {
        // 解析报文头信息
        log.info("解析结果{}",cat1MessageDto);
        Date now = new Date();
        String meterNumber = FlowMeterUtils.stringHeightLowConvert(cat1MessageDto.getMeterNumber());
        QueryWrapper<MeterDevice> queryWrapper = new QueryWrapper<MeterDevice>().eq("meter_number",meterNumber);
        MeterDevice meterDevice = meterDeviceService.getOne(queryWrapper);
        if(meterDevice == null){
            return;
        }
        String body = cat1MessageDto.getBody();
        QueryWrapper<Meter> meterQueryWrapper = new QueryWrapper<Meter>().eq("meter_number",meterNumber);
        Meter meter = meterService.getOne(meterQueryWrapper);
        MeterDeviceDataLog meterDeviceDataLog = new MeterDeviceDataLog();
        meterDeviceDataLog.setUploadJson(body);
        meterDeviceDataLog.setCreateTime(now);
        meterDeviceDataLog.setTenantId(meterDevice.getTenantId());
        meterDeviceDataLog.setMeterNumber(meterDevice.getMeterNumber());
        meterDeviceDataLogService.save(meterDeviceDataLog);
        //body解析
        BigDecimal totalFlow = BigDecimal.ZERO;
        BigDecimal bcdFlow = BigDecimal.valueOf(Integer.parseInt(FlowMeterUtils.stringHeightLowConvert(body.substring(8,16)))).divide(new BigDecimal(100),2,RoundingMode.HALF_UP);
        switch (body.substring(6,8)){
            case "2B": //单位是L
                totalFlow = bcdFlow.scaleByPowerOfTen(2).divide(new BigDecimal(1000),2,RoundingMode.HALF_UP);
                break;
            case "29":
                totalFlow = bcdFlow.divide(new BigDecimal(1000),2,RoundingMode.HALF_UP);
                break;
            case "2A":
                totalFlow = bcdFlow.scaleByPowerOfTen(1).divide(new BigDecimal(1000),2,RoundingMode.HALF_UP);
                break;
            case "2C":
                totalFlow = bcdFlow;
                break;
            case "2D":
                totalFlow = bcdFlow.scaleByPowerOfTen(1);
                break;
            case "2E":
                totalFlow = bcdFlow.scaleByPowerOfTen(2);
                break;
        }
        //反向累计流量
        BigDecimal reverseTotalFlow = BigDecimal.ZERO;
        BigDecimal bcdReverseFlow = BigDecimal.valueOf(Integer.parseInt(FlowMeterUtils.stringHeightLowConvert(body.substring(28,36)))).divide(new BigDecimal(100),2,RoundingMode.HALF_UP);
        switch (body.substring(26,28)){
            case "2B": //单位是L
                reverseTotalFlow = bcdReverseFlow.scaleByPowerOfTen(2).divide(new BigDecimal(1000),2,RoundingMode.HALF_UP);
                break;
            case "29":
                reverseTotalFlow = bcdReverseFlow.divide(new BigDecimal(1000),2,RoundingMode.HALF_UP);
                break;
            case "2A":
                reverseTotalFlow = bcdReverseFlow.scaleByPowerOfTen(1).divide(new BigDecimal(1000),2,RoundingMode.HALF_UP);
                break;
            case "2C":
                reverseTotalFlow = bcdReverseFlow;
                break;
            case "2D":
                reverseTotalFlow = bcdReverseFlow.scaleByPowerOfTen(1);
                break;
            case "2E":
                reverseTotalFlow = bcdReverseFlow.scaleByPowerOfTen(2);
                break;
        }
        JSONObject flowJson = new JSONObject();
        flowJson.put("rtFlow",reverseTotalFlow);
        BigDecimal  velocity = BigDecimal.ZERO;
        BigDecimal bcdVelocity = BigDecimal.valueOf(Integer.parseInt(FlowMeterUtils.stringHeightLowConvert(body.substring(48,56)))).divide(new BigDecimal(100),2,RoundingMode.HALF_UP);
        switch (body.substring(46,48)){
            //流速单位
            case "35"://立方米每小时
                velocity = bcdVelocity;
                break;
            case "36":
                velocity = bcdVelocity.scaleByPowerOfTen(1);
                break;
            case "37":
                velocity = bcdVelocity.scaleByPowerOfTen(2);
                break;
        }
        flowJson.put("velocity",velocity);
        //温度
        BigDecimal temperature = BigDecimal.valueOf(Integer.parseInt(FlowMeterUtils.stringHeightLowConvert(body.substring(56,62)))).divide(new BigDecimal(100),2,RoundingMode.HALF_UP);
        meterDevice.setTemperature(temperature.doubleValue());
        //压力 单位MPa
        BigDecimal pa = BigDecimal.valueOf(Integer.parseInt(FlowMeterUtils.stringHeightLowConvert(body.substring(62,66)))).divide(new BigDecimal(1000),2,RoundingMode.HALF_UP);
        //表状态解析
        meterDevice.setMeterStatusCode(body.substring(86,88));
        //信号强度
        meterDevice.setSignalValue(Integer.parseUnsignedInt(body.substring(88, 92), 16) - 0xFFFF -1);
        //iccid
        if(StringUtils.isBlank(meterDevice.getIccId())){
            meterDevice.setIccId(body.substring(100,120));
        }
        //imei
        if(StringUtils.isBlank(meterDevice.getImei())){
            meterDevice.setImei(body.substring(121,136));
        }
        //各个时间段负增量 FFFF是还没有开始累计 0000 是0的反增量 16进制 保留 0002 0.2 02ff 2*16*16+16*15+15/10=76.7
        flowJson.put("tadd",body.substring(156));
        meterDevice.setLastUploadTime(now);
        meterDevice.setFlowJson(JSONObject.toJSONString(flowJson));
        double lastReadingValue = Optional.ofNullable(meterDevice.getAmount()).orElse(0d);
        double useWater = totalFlow.doubleValue() - lastReadingValue;
        //异常新增
        addAlarm(meter,meterDevice,now,useWater);
        meterDevice.setAmount(totalFlow.doubleValue());
        meterDeviceService.updateById(meterDevice);
        //新增每天存一条日志，调用smartwater中的接口传入device和lastAmount 进行统一到队列中处理
//                String url = "https://9499if37rw64.vicp.fun/meter/device/log/add";
        String url = "http://localhost:8086/meter/device/log/add";
        String url2 = "http://http://47.99.84.199:8086/meter/device/log/add";
        meterDevice.setLastAmount(BigDecimal.valueOf(lastReadingValue));
        //调用接口更新数据信息
        RestUtil.post(url, (JSONObject) JSONObject.toJSON(meterDevice));
        RestUtil.post(url2, (JSONObject) JSONObject.toJSON(meterDevice));
        if(meter != null){
            meter.setUpdateTime(now);
            meter.setLastReadingTime(now);
            meter.setValveStatus(meterDevice.getValveStatus());
            meter.setLastReading(BigDecimal.valueOf(meterDevice.getAmount()).multiply(BigDecimal.valueOf(100)).intValue()); //止码
            meterService.updateById(meter);
        }
        sendInstruction(ctx,meterNumber);
    }



    public void addAlarm(Meter meter, MeterDevice meterDevice, Date time, double useWater){
        long readTime = time.getTime();
        long lastTime = meter != null ?
                meter.getLastReadingTime() != null ? meter.getLastReadingTime().getTime() : meter.getCreateTime().getTime() :
                meterDevice.getLastUploadTime() != null ? meterDevice.getLastUploadTime().getTime() : meterDevice.getCreateTime().getTime();
        long diffInMilliseconds = Math.abs(readTime - lastTime);
        if(meter == null && diffInMilliseconds >= (60 * 60 * 1000) && useWater > 0 && meterDevice.getAmount() != null && meterDevice.getAmount() > 10){
            saveMeterAlarmLog(5,useWater,null,meterDevice,time);
        }
        if(diffInMilliseconds >= (60 * 60 * 1000)){
            int status = Integer.parseInt(meterDevice.getMeterStatusCode(),16);
            int voltageStatus = isBitInt(status,0); //0:正常 1：报警
            int kongGuanStatus = isBitInt(status,1);
            int reverseStatus = isBitInt(status,2);
            int excessStatus = isBitInt(status,3);
            int waterTemperatureStatus = isBitInt(status,4);
            int eeStatus = isBitInt(status,5);
            int paStatus = isBitInt(status,7);
            if(voltageStatus == 1){
                //电压异常
                saveMeterAlarmLog(2,useWater,meter,meterDevice,time);
            }
            if(kongGuanStatus == 1){ //空管异常
                saveMeterAlarmLog(6,useWater,meter,meterDevice,time);
            }
            if(reverseStatus == 1){ //反流异常
                saveMeterAlarmLog(7,useWater,meter,meterDevice,time);
            }
            if(excessStatus == 1){ //超量异常
                saveMeterAlarmLog(8,useWater,meter,meterDevice,time);
            }
            if(waterTemperatureStatus == 1){ //水温异常
                saveMeterAlarmLog(9,useWater,meter,meterDevice,time);
            }
            if(eeStatus == 1){ //ee异常
                saveMeterAlarmLog(10,useWater,meter,meterDevice,time);
            }
            if(paStatus == 1){ //压力异常
                saveMeterAlarmLog(11,useWater,meter,meterDevice,time);
            }
        }
    }

    public void sendInstruction(ChannelHandlerContext ctx,String meterNumber){
        String instruction = "6841"+FlowMeterUtils.stringHeightLowConvert(meterNumber)+"04A11900"+FlowMeterUtils.getCurrentTimeFormatted()+"0000000000";
        String checksum = FlowMeterUtils.getChecksum(instruction);
        instruction = instruction+checksum+"16";
        byte[] msg = toBytes(instruction);
        log.info("发送指令应答：{}",instruction);
        ctx.writeAndFlush(ctx.alloc().buffer(msg.length).writeBytes(msg));
    }

    public void saveMeterAlarmLog(int alarmType, double useWater, Meter meter, MeterDevice meterDevice, Date time) {
        QueryWrapper<MeterAlarmLog> meterAlarmLogQueryWrapper = new QueryWrapper<>();
        //同一个异常类型同一个表不能有重复的数据生成
        meterAlarmLogQueryWrapper.eq("meter_number",meterDevice.getMeterNumber());
        meterAlarmLogQueryWrapper.eq("alarm_type",alarmType);
        meterAlarmLogQueryWrapper.eq("status",0); //状态是待处理
        MeterAlarmLog meterAlarmLog = meterAlarmLogService.getOne(meterAlarmLogQueryWrapper);
        //有则修改修改时间，没有则新增
        if(meterAlarmLog != null){
            meterAlarmLog.setWaterVolume(alarmType == 5 ? meterDevice.getAmount() + useWater : useWater);
            meterAlarmLog.setUpdateTime(time);
            meterAlarmLog.setAlarmCount(meterAlarmLog.getAlarmCount()+1);
            meterAlarmLogService.updateById(meterAlarmLog);
        }else{
            meterAlarmLog = new MeterAlarmLog();
            meterAlarmLog.setAlarmCount(1);
            if(meter != null){
                meterAlarmLog.setTenantId(meter.getTenantId());
                meterAlarmLog.setMeterId(meter.getId());
                meterAlarmLog.setCustomerId(meter.getCustomerId());
            }else{
                meterAlarmLog.setTenantId(meterDevice.getTenantId());
            }
            meterAlarmLog.setMeterNumber(meterDevice.getMeterNumber());
            meterAlarmLog.setWaterVolume(alarmType == 5 ? meterDevice.getAmount()+useWater : useWater);
            meterAlarmLog.setAlarmType(alarmType);
            meterAlarmLog.setCreateTime(time);
            meterAlarmLogService.save(meterAlarmLog);
        }
    }
}
